
Type CalSpringSystem

	Field m_model:CalModel
	Field m_gravity:CalVector
	Field m_force:CalVector
	Field m_collision:Byte
	
	Method New()
		m_model = Null
		m_gravity = New CalVector
		m_force = New CalVector
		m_collision = False
	End Method
	
	Function FromModel:CalSpringSystem(model:CalModel)
		Local ss:CalSpringSystem = New CalSpringSystem
		ss.m_model = model
		ss.m_gravity = CalVector.FromCoords(0, -98.1, 0)
		ss.m_force = CalVector.FromCoords(0, 0,-0.5)
		ss.m_collision = False	
		Return ss
	End Function
	
	Method calculateForces(submesh:CalSubmesh, deltaTime:Float)
	
		' get the vertex vector of the submesh
		Local vectorVertex:CalVector[] = submesh.getVectorVertex()
	
		' get the vertex vector of the submesh
		Local vectorPhysicalProperty:CalPhysicalProperty[] = submesh.getVectorPhysicalProperty()
	
		' get the physical property vector of the core submesh
		Local vectorCorePhysicalProperty:CalPhysicalProperty[] = submesh.getCoreSubmesh().getVectorPhysicalProperty()
	
		' loop through all the vertices
		For Local vertexId:Int = 0 To vectorVertex.length-1
			' get the physical property of the vertex
			Local physicalProperty:CalPhysicalProperty = vectorPhysicalProperty[vertexId]
	
			' get the physical property of the core vertex
			Local corePhysicalProperty:CalPhysicalProperty = vectorCorePhysicalProperty[vertexId]
	
			' only take vertices with a weight > 0 into account
			If corePhysicalProperty.weight > 0 Then
				' vertex_force = force + gravity * weight
				physicalProperty.force.assignvector(m_gravity)
				physicalProperty.force.multScalarLocal(corePhysicalProperty.weight)
				physicalProperty.force.addLocal(m_force)
			EndIf
		Next		
	
	End Method
	
	Method calculateVertices(submesh:CalSubmesh, deltaTime:Float)
		
		' get the vertex vector of the submesh
		Local vectorVertex:CalVector[] = submesh.getVectorVertex()
	
		' get the physical property vector of the submesh
		Local vectorPhysicalProperty:CalPhysicalProperty[] = submesh.getVectorPhysicalProperty()
	
		' get the physical property vector of the core submesh
		Local vectorCorePhysicalProperty:CalPhysicalProperty[] = submesh.getCoreSubmesh().getVectorPhysicalProperty()
	
		' loop through all the vertices
		For Local vertexId:Int = 0 To vectorVertex.length-1
			' get the vertex
			Local vertex:CalVector = vectorVertex[vertexId]
	
			' get the physical property of the vertex
			Local physicalProperty:CalPhysicalProperty = vectorPhysicalProperty[vertexId]
	
			' get the physical property of the core vertex
			Local corePhysicalProperty:CalPhysicalProperty = vectorCorePhysicalProperty[vertexId]
	
			' store current position for later use
			Local position:CalVector = CalVector.FromVector(physicalProperty.position)
	
			' only take vertices with a weight > 0 into account
			If corePhysicalProperty.weight > 0 Then
				' do the Verlet step
				physicalProperty.position.addLocal( vectorSub(position, physicalProperty.positionOld).multScalarLocal(0.99).addLocal(vectorScalarMult(physicalProperty.force, deltaTime * deltaTime / corePhysicalProperty.weight)) )
	
				Local skeleton:CalSkeleton = m_model.getSkeleton()
	
				' collision detection and response
				If m_collision = True Then
					Local vectorBone:CalBone[] = skeleton.getVectorBone()
	
					For Local boneId:Int = 0 To vectorBone.length-1
						Local p:CalBoundingBox = vectorBone[boneId].getBoundingBox()
						Local isIn:Byte = True
						Local Mini:Int = $1e10
						Local index:Int = -1
	
						For Local faceId:Int = 0 To 5			
							If p.plane[faceId].eval(physicalProperty.position) <= 0 Then
								isIn = False
							Else
								Local dist:Float = p.plane[faceId].dist(physicalProperty.position)
								If dist < Mini Then
									index = faceId
									Mini = dist
								EndIf
							EndIf
						Next
	
						If isIn = True And index <> -1 Then
							Local normal:CalVector = CalVector.FromCoords(p.plane[index].a, p.plane[index].b, p.plane[index].c)
							normal.normalize()
							physicalProperty.position.subLocal(vectorScalarMult(normal, Mini))
						EndIf
	
						isIn = True
	
						For faceId = 0 To 5				
							If p.plane[faceId].eval(physicalProperty.position) < 0 Then
								isIn = False				
							EndIf
						Next
						If isIn = True Then
							physicalProperty.position.assignvector(vectorVertex[vertexId])
						EndIf
					Next
				EndIf
			Else
				physicalProperty.position.assignvector(vectorVertex[vertexId])
			EndIf
	
			' make the current position the old one
			physicalProperty.positionOld.assignvector(position)
	
			' set the new position of the vertex
			vertex.assignvector(physicalProperty.position)
	
			' clear the accumulated force on the vertex
			physicalProperty.force.clear()
		Next
	
		' get the spring vector of the core submesh
		Local vectorSpring:CalSpring[] = submesh.getCoreSubmesh().getVectorSpring()
	
		' iterate a few times to relax the constraints
		Local TOTAL_ITERATION_COUNT:Int = 2
		For Local iterationCount:Int = 0 To TOTAL_ITERATION_COUNT-1
			' loop through all the springs
			For Local springId:Int = 0 To vectorSpring.length-1
				' get the spring
				Local spring:CalSpring = vectorSpring[springId]
	
				' compute the difference between the two spring vertices
				Local distance:CalVector = vectorSub(vectorVertex[spring.vertexId[1]], vectorVertex[spring.vertexId[0]])
	
				' get the current Length of the spring
				Local Length:Float = distance.length()
	
				If Length > 0 Then
					Local factor:Float[2]
					factor[0] = (Length - spring.idleLength) / Length
					factor[1] = factor[0]
	
					If vectorCorePhysicalProperty[spring.vertexId[0]].weight > 0 Then
						factor[0] = factor[0]/2
						factor[1] =factor[1]/2
					Else
						factor[0] = 0
					EndIf
	
					If vectorCorePhysicalProperty[spring.vertexId[1]].weight <= 0 Then
						factor[0] = factor[0]*2
						factor[1] = 0
					EndIf
	
					vectorVertex[spring.vertexId[0]].addLocal(vectorScalarMult(distance, factor[0]))
					vectorPhysicalProperty[spring.vertexId[0]].position.assignvector(vectorVertex[spring.vertexId[0]])
	
					vectorVertex[spring.vertexId[1]].subLocal(vectorScalarMult(distance, factor[1]))
					vectorPhysicalProperty[spring.vertexId[1]].position.assignvector(vectorVertex[spring.vertexId[1]])
					'}
				EndIf
			Next
		Next	
	End Method
	
	Method update(deltaTime:Float)
	
		' get the attached meshes vector
		Local vectorMesh:CalMesh[] = m_model.getVectorMesh()
	
		' loop through all the attached meshes
		For Local meshId:Int = 0 To vectorMesh.length-1
			' get the ssubmesh vector of the mesh
			Local vectorSubmesh:CalSubmesh[] = vectorMesh[meshId].getVectorSubmesh()
	
			' loop through all the submeshes of the mesh
			For Local submeshId:Int = 0 To vectorSubmesh.length-1
				Local submesh:CalSubmesh = vectorSubmesh[submeshId]
	
				' check if the submesh contains a spring system
				If submesh.getCoreSubmesh().getSpringCount() > 0 And submesh.hasInternalData() Then
					' calculate the new forces on each unbound vertex
					calculateForces(submesh, deltaTime)
	
					' calculate the vertices influenced by the spring system
					calculateVertices(submesh, deltaTime)
				EndIf
			Next
		Next
	
	End Method
	
	Method getGravityVector:CalVector()
		Return m_gravity
	End Method
	
	Method setGravityVector(gravity:CalVector)
		m_gravity.assignvector(gravity)
	End Method
	
	Method getForceVector:CalVector()
		Return m_force
	End Method
	
	Method setForceVector(force:CalVector)
		m_force.assignvector(force)
	End Method
	
	Method setCollisionDetection(collision:Byte)
		m_collision = collision
	End Method

End Type
